; Analysis of J023 SCPUA Z80+FDC board CP/M boot ROM. ; ; 2022-11-08 Initial examination ; ; Compiles online at https://www.asm80.com/ ; ; 2022-11-13 Added conditional assembly for FDC chips ; with true or inverted data bus. ; For true data bus, the inverting instructions are removed ; and nop codes added after the routines ; to minimise code address disruption. ; ; NB the 2795 and 2797 chips are incompatible with the SCPUA ; due to pin 25 difference. ; fdc_data_bus_inverted equ 1 ; equ 1 for WD1793, or 0 for WD2793 .cpu z80 .org 0 .binfrom 0 .binto $2000 ; ; Common logic bit masks ; mask_bit_0 equ 1 mask_bit_1 equ 2 mask_bit_2 equ 4 mask_bit_3 equ 8 mask_bit_4 equ $10 mask_bit_5 equ $20 mask_bit_6 equ $40 mask_bit_7 equ $80 ; ; ASCII stuff ; ASC_BEL EQU 7 ASC_CR EQU 0dh ASC_LF EQU 0ah ASC_SP EQU 20h ; ; CP/M stuff ; CPM_TPA equ $0100 L4000h equ $4000 ; 16K above boot RAM ; L8000h equ $8000 ; RAM always in 8000 to FFFF ; ; To make a BDOS call in an 8-bit CP/M, use: ; ; LD DE,parameter ; LD C,function ; CALL 5 ; ; ; Some system variables have to be kept here, ; so they don't vanish when memory below $8000 ; is paged in and out. ; ; ; control_latch_remembered equ L8000h+$14 SIDES_remembered equ L8000h+$15 SYSTRK_remembered equ L8000h+$16 L8017h equ L8000h+$17 L8018h equ L8000h+$18 L8019h equ L8000h+$19 L801ah equ L8000h+$1a L801eh equ L8000h+$1e L80a1h equ L8000h+$a1 ; ; ; ; Table 5. On-Board IO Device Addresses ; control_latch equ 0 ; write control latch keyboard equ 0 ; read parallel keyboard port ; ; Serial ports ; scc_port_b_control equ 4 ; Port B Control SCC scc_port_b_status equ 4 ; Port B Control SCC scc_port_a_control equ 5 ; Port A scc_port_a_status equ 5 ; Port A scc_port_b_data equ 6 ; Port B Data scc_port_a_data equ 7 ; Port A ; ; Floppy Disk controller ; fdc_reg_0_command equ 8 ; Register 0 write fdc_reg_0_status equ 8 ; Register 0 read fdc_reg_1_track equ 9 ; Register 1 fdc_reg_2_sector equ 10 ; Register 2 fdc_reg_3_data equ 11 ; Register 3 ; C-F Empty (supposedly!) fdc_reg_0_command_alias equ $c ; $10 to $7F Aliased addresses of onboard devices ; $80 to $FF Offboard IO addresses via STEbus ; $100 to $FFF Aliased IO addresses 0 to 255 ; Onboard IO addresses are repeated whenever A7 = 0. ; Offboard addresses accessed whenever A7 = 1. ; Disk Controller Commands: ; 7 6 5 4 3 2 1 0 Command ; 0 0 0 0 x x x x Restore to track 0 ; 0 0 0 1 x x x x Seek ; 0 0 1 x x x x x Step ; 0 1 0 x x x x x Step in ; 0 1 1 x x x x x Step out ; Bits: ; 4 - 0:No update of track reg ; 1:Update track register ; 3 - 0:Unload head at start ; 1:Load head at start ; 2 - 0:No verify of track no ; 1:Verify track no. on disc ; 1-0 Read as 2-bit stepping rate: ; 00 = 6ms ; 01 = 12ms ; 10 = 20ms ; 11 = 30ms ; 1 0 0 x x x x 0 Read sector ; 1 0 1 x x x x x Write sector ; 1 1 0 0 0 x x 0 Read address ; 1 1 1 0 0 x x 0 Read track ; 1 1 1 1 0 x x 0 Write track ; Bits: ; 4 - 0:Read/write 1 sector ; 1:Read all sectors till the end of a track. ; ; 3 - Interpretation of 2 bit sector length field in sector header: ; 0: Field is interpreted as ; 00 = 256 bytes/sector ; 01 = 512 bytes/sector ; 10 = 1024 bytes/sector ; 11 = 128 bytes/sector ; 1: Field is interpreted as ; 00 = 128 bytes/sector ; 01 = 256 bytes/sector ; 10 = 512 bytes/sector ; 11 = 1024 bytes/sector ; (set to 1 on Dragon) ; ; 2 - 0:No head loading delay ; 1:Head loading delay of 30ms prior to read/writes. ; ; 1 - 0:Set side select o/p to 0 ; 1:Set side select o/p to 1 ; ; 0 - 0:Write Data Address Mark ; 1:Write Deleted Data Address mark ; 1 1 0 1 x x x x Force Interrupt Generate an interrupt & terminate the current operation on: ; Bits set: ; 0 - Drive status transition Not-Ready to Ready ; 1 - Drive status transition Ready to Not-Ready ; 2 - Index pulse ; 3 - Immediate interrupt ; Bits clear: ; No interrupt occurs, all operations terminated. ($D0) ; ; Status ; ; Status (read), when set: ; Status bits may have different meanings depending on the driver operation being performed. ; mask_bit_Drive_busy equ 1 mask_bit_Data_Request_or_Index equ 2 mask_bit_Lost_Data_Track_00 equ 4 mask_bit_CRC_error equ 8 mask_bit_Record_Not_Found_Seek_Err equ $10 mask_bit_Data_Address_Mark equ $20 ; 0:Data Address Mark read ; 1:Deleted Data Address Mark read OR Head Loaded mask_bit_Write_Protect equ $40 mask_bit_Not_Ready equ $80 ; ; Start of code in manual ; RESET: ; 0000H jp START ; reset entry point ; reserved space for interrupt/restart jumps DEFB 0FFH DEFW 0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH ; ; ; .fill $ff, $0040 - $ ; ; Boot configuration data ; .org $0040 ; ; 0040H ; Set the default baud rate for the console. Allowed values are ; 1=50 baud, 2=75 baud, 3=110 baud, 4=134.5 baud, 5=150 baud, ; 6=300 baud, 7=600 baud, 8=1200 baud, 9=1800 baud, 10=2400 baud, ; 11=3600 baud, 12=4800 baud, 13=7200 baud, 14=9600 baud. ; BDEF: DEFB 14 ; default is 9600 ; ; Enable/disable baud rate sensing ; Set 0 to disable this facility, FFH to enable it. ; AUTO: DEFB 0FFH ; FF to enable autobaud ; ; disk drive data ; ; Type: bits 7,6,5 and 5 correspond to the bit settings ; to load into the latch at I/O address 0 to select the drive. ; Bit 7: 1=8", O=5.25" or 3.5" ; 6: drive select 1, active low ; 5: drive select 0, active low ; 4: must be 1 ; 3: 1-single density, 0=double density ; 2: must be 0 ; 1: must be 0 ; 0: must be 0 ; TYPE: DEFB 050h ; disk control bits ; ; Set 0 for a single sided drive, 1 for a double sided drive ; SIDES: DB 1 ; double sided ; ; Set to the step speed bits for FDC (279x) type I command ; 0 = 3ms ; 1 = 6ms ; 2 = 10ms ; 3 = 15ms ; STEPS: DB 0 ; step speed bits ; ; Set to the number of tracks on the drive ; 5.25" = 80 ; 3.5m = 80 ; NTRKS: DB 80 ; number of tracks ; ; CP/M Plus style disk parameter block for 3.5-inch drive. ; For this control block each side of a physical track counts ; as a separate logical track. ; SECPT: DEFW 36 ; logical records / track DEFB 5,1FH ; block shift, mask DEFB 3 ; extent mask DEFW 177 ; maximum block number DEFW 127 ; max directory entry DEFB 080H,000H ; directory block mask DEFW 32 ; directory check vector SYSTRK: DEFW 1 ; number of system tracks SECSIZ: DEFB 2,3 ; physical shift, mask ; DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use DEFB 0FFH ; reserved for future use ; ; reserved for NMI jump and user patches ; 0060H ; DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH DEFW 0FFFFH,0FFFFH,0FFFFH,0FFFFH .fill $ff, $0080 - $ ; The low-level character I/O routines are contained in the SCPUA board boot EPROM, ; so that the bootstrap program and disk operating system are independent of the exact hardware configuration. ; All the routines are accessed through a jump table at location 80H in the EPROM. ; The structure of this jump table is as follows: ; ; ; Labels: ; CISO: equ 0x0127 ; COO: equ 0x0157 ; CIO: equ 0x0187 ; COSO: equ 0x01b7 ; CITO: equ 0x00D7 ; Jump table ; 0080H .org $0080 ;JUMP_TABLE: ; ; character I/O routines ; JPCIT0: jp CIT0 ;0080 ; initialise JPCIS0: jp CIS0 ;0083 ; input status JPCI0: jp CI0 ;0086 ; input JPCOS0: jp COS0 ;0089 ; output status JPCO0: jp CO0 ;008c ; output ; The jumps for the character I/O routines point at additional jump tables, ; allowing support for a total of 16 character I/O devices. ; These jump tables look like: ; ; CITO: JP CINIO ; initialise device 0 ; JP CINIT ; initialise device 1 ; JP CINI2 ; initialise device 2 ; etc. ; ; CISO: JP CISTO ; input status device 0 ; JP CIST1 ; input status device 1 ; JP CIST2 ; input status device 2 ; etc. ; ; CIO; JP CINO ; input from device 0 ; JP CIN1 ; input from device 1 ; JP CIN2 ; input from device 2 ; etc. ; COSO: ; JP COSTO ; output status device 0 ; JP COST1 ; output status device 1 ; JP COST2 ; output status device 2 ; etc. ; ; COO: JP COUTO ; output to device 0 ; JP COUT1 ; output to device 1 ; JP COUT2 ; output to device 2 ; etc. ; ; The character I/O routines allow access to the on-board SCC and the on-board parallel input port ; (designated the keyboard port, though its use is much more general). ; Device 0 is SCC channel A, device 1 is SCC channel B and input device 2 is the keyboard port. ; The keyboard port expects a short negative going strobe when the data are available. ; The data are latched on the rising edge of this strobe. ; ; CINI0 through CINI15: the jump table starting address is stored at 81H. ; Initialise a character device. ; This is called with A = baud rate (0 implies no change, ; 1 through 15 correspond to 50 through 19200 baud as used by CP/M Plus). ; Please note that 19200 baud is not supported for the SCPUA on-board SCC. ; No registers other than A are changed. ; ; CIST0 through CIST15: the jump table starting address is stored at 84H. ; Input status of a character device. ; This returns A 0 if no character is available, ; or A = FFH if a character is available. ; No other registers are changed. ; ; CIN0 through CIN15: the jump table starting address is stored at 87H. ; Input a character. ; This returns in A the next available character from this device. ; The character is an 8-bit value, except for device 0, ; for which the character is assumed to be ASCII and only a 7-bit value is returned. ; No register other than A is changed. ; ; COST0 through COST15: the jump table starting address is stored at 8AH. ; Output status of a character device. ; This returns A = 0 if the transmitter is still busy, ; or A = FFH if the transmitter is ready to accept another character. ; No other register is changed. ; ; COUT0 through COUT15: the jump table starting address is stored at 8DH. ; Output a character. This outputs the 8-bit value in A. No register is changed. jump_to_out_fdc_reg_0: jp out_fdc_reg_0 jump_to_inp_fdc_reg_0: jp inp_fdc_reg_0 jump_to_out_fdc_reg_1_track: jp out_fdc_reg_1_track jump_to_inp_fdc_reg_1_track: jp inp_fdc_reg_1_track jump_to_out_fdc_reg_2_sector: jp out_fdc_reg_2_sector jump_to_inp_fdc_reg_2_sector: jp inp_fdc_reg_2_sector jump_to_out_fdc_reg_3_data: jp out_fdc_reg_3_data jump_to_inp_fdc_reg_3_data: jp inp_fdc_reg_3_data ; jump_to_fdc_fn_command_sector_read: jp fdc_fn_command_sector_read jump_to_fdc_fn_command_sector_write: jp fdc_fn_command_sector_write jump_to_fdc_fn_write_command_to_alias: jp fdc_fn_write_command_to_alias jump_to_fdc_fn_write_command: jp fdc_fn_write_command ; ; ; sign_on_message_start: .db "Copyright (C) 1984, Arcom Control Systems Ltd." .db "841023" CIT0: ; equ 0x00e7 ; jp CINI0 ; device 0 = console jp CINI1 ; device 1 = printer jp CINI2 ; device 2 = keyboard jp CINI2 ; reserved for devices 3 to 16 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 jp CINI2 CIS0: jp CIST0 ; device 0 = console jp CIST1 ; device 1 = printer jp CIST2 ; device 2 = keyboard jp CIST3 ; reserved for devices 3 to 16 jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; jp CIST3 ; CI0: jp CIN0 ; device 0 = console jp CIN1 ; device 1 = printer jp CIN2 ; device 2 = keyboard jp CIN3 ; reserved for devices 3 to 16 jp CIN3 ;0153 jp CIN3 ;0156 jp CIN3 ;0159 jp CIN3 ;015c jp CIN3 ;015f jp CIN3 ;0162 jp CIN3 ;0165 jp CIN3 ;0168 jp CIN3 ;016b jp CIN3 ;016e jp CIN3 ;0171 jp CIN3 ;0174 COS0: jp COST0 ; device 0 = console jp COST1 ; device 1 = printer jp COST2 ; device 2 = keyboard jp COST2 ;0180 ; reserved for devices 3 to 16 jp COST2 ;0183 jp COST2 ;0186 jp COST2 ;0189 jp COST2 ;018c jp COST2 ;018f jp COST2 ;0192 jp COST2 ;0195 jp COST2 ;0198 jp COST2 ;019b jp COST2 ;019e jp COST2 ;01a1 jp COST2 ;01a4 CO0: jp COUT0 ; device 0 = console jp COUT1 ; device 1 = printer jp COUT2 ; device 2 = keyboard jp COUT2 ; reserved for devices 3 to 16 jp COUT2 ;01b3 jp COUT2 ;01b6 jp COUT2 ;01b9 jp COUT2 ;01bc jp COUT2 ;01bf jp COUT2 ;01c2 jp COUT2 ;01c5 jp COUT2 ;01c8 jp COUT2 ;01cb jp COUT2 ;01ce jp COUT2 ;01d1 jp COUT2 ;01d4 data_01d7: scc_port_a_initialisation_data: .db $09,$80 ; register, data pairs .db $04,$4C .db $01,$01 .db $03,$C0 .db $05,$62 .db $09,$00 .db $0A,$00 .db $0B,$56 ; .db $0E,$02 .db $0E,$03 .db $03,$C1 .db $05,$EA .db $0F,$08 .db $00,$10 .db $00,$10 ; reset twice to be sure scc_port_b_initialisation_data: .db $09,$40 .db $04,$4C .db $03,$E0 .db $05,$62 .db $09,$00 .db $0A,$00 .db $0B,$56 ; .db $0E,$02 .db $0E,$03 .db $03,$E1 .db $05,$EA .db $0F,$00 .db $00,$10 .db $00,$10 ; reset twice to be sure ; ; Time Constant = PCLK or RTxC Frequency ; ---------------------- -2 ; 2(Baud Rate)(Clock Mode) ; ; PCLK is 4 MHz, clock mode is 16. ; So TC = PCLK/1600 - ; ; table_of_baud_rate_divisors: ; 32 data bytes .dw 00 ; zero baud rate .dw 2500 ; 50 baud (missed the -2 from equation?) .dw 1665 ; 75 baud .dw 1134 ; 110 baud .dw 924 ; 134.5 baud .dw 831 ; 150 baud .dw 415 ; 300 baud .dw 206 ; 600 baud .dw 102 ; 1200 baud .dw 67 ; 1800 baud .dw 50 ; 2400 baud .dw 33 ; 3600 baud .dw 24 ; 4800 baud .dw 15 ; 7200 baud .dw 11 ; 9600 baud .dw 5 ; 19200 baud .org 0231h ; ; looks like start of legitimate code here: ; ; 0231h: block_output_to_c: otir ;0231 add a,a ;0233 jr z,block_output_more ; ; set baud rate ; push hl ; save hl ld e,a ; de = a, an index value ld d,0 ; ld hl,table_of_baud_rate_divisors ;023a add hl,de ; hl += de ; ld b,12 ; SCC register 12+13 are the baud rate divisor ld a,(hl) ;0240 out (c),b ; ouput register out (c),a ; output data byte inc b ; next register inc hl ; next data ld a,(hl) ; get data out (c),b ; ouput register out (c),a ; output data byte pop hl ; restore hl ; block_output_more: pop bc ;024d otir ;024e ; pop bc ; restore registers pop de ; pop hl ; ret ; ; ; CINI0: push hl ;0254 push de ;0255 push bc ;0256 ; ld c,scc_port_a_control ld b,14 push bc ;025b ; ld b,16 ; 16 bytes = 8 register-data pairs ld hl,scc_port_a_initialisation_data jr block_output_to_c ; ; ; CIST0: in a,(scc_port_a_status) and mask_bit_0 ret z ; or 0ffh ret ; ; ; CIN0: call CIST0 ; jr z,CIN0 ; ignore null character in a,(scc_port_a_data) ; and %01111111 ; 7-bit ascii only ret ; ; ; COST0: in a,(scc_port_a_status) and mask_bit_2 ; ret z ; or 0ffh ret ; ; ; COUT0: push af COUT0_waiting: call COST0 jr z,COUT0_waiting pop af out (scc_port_a_data),a ret ; ; ; CINI1: push hl push de push bc ld c,scc_port_b_control ld b,14 push bc ld b,14 ld hl,scc_port_b_initialisation_data jr block_output_to_c ; ; ; CIST1: in a,(scc_port_b_control) and mask_bit_0 ret z ; or 0ffh ret ; ; ; CIN1: call CIST1 jr z,CIN1 in a,(scc_port_b_data) ret ; ; ; COST1: in a,(scc_port_b_control) and mask_bit_2 ret z ; or 0ffh ret ; ; ; COUT1: push af COUT1_waiting: call COST1 jr z,COUT1_waiting pop af out (scc_port_b_data),a ret ; ; Keyboard input status ; CIST2: ld a,3 out (scc_port_a_control),a in a,(scc_port_a_status) and mask_bit_0 ret z ; in a,(scc_port_a_status) xor mask_bit_3 and mask_bit_3 jr z,CIN2_x or 0ffh ret ; ; Keyboard input ; CIN2: call CIST2 jr z,CIN2 in a,(keyboard) ; CIN2_x: push af ld a,010h out (scc_port_a_control),a pop af ret ; ; ; CINI2: ; never able to input ret ; ; ; CIST3: ; always able to input xor a ; a = a xor a = 0 ret ; ; ; CIN3: ld a,01ah ; is ascii "SUB" value ret ; ; ; COST2: ld a,0ffh ; never able to output ret ; ; Keyboard is not an output device ; COUT2: ; never able to output ret ; ; the cpl instructions invert the data because ; the fdc chip has an inverted bus ; out_fdc_reg_0: .if fdc_data_bus_inverted cpl ; for fdc data bus inversion out (fdc_reg_0_command),a cpl ; for fdc data bus inversion ret .else out (fdc_reg_0_command),a ret nop ; padding nop ; padding .endif ; ; ; inp_fdc_reg_0: in a,(fdc_reg_0_status) .if fdc_data_bus_inverted cpl ret .else ret nop ; padding .endif ; ; ; out_fdc_reg_1_track: .if fdc_data_bus_inverted cpl ; for fdc data bus inversion out (fdc_reg_1_track),a cpl ; for fdc data bus inversion ret .else out (fdc_reg_1_track),a ret nop ; padding nop ; padding .endif ; ; ; inp_fdc_reg_1_track: in a,(fdc_reg_1_track) .if fdc_data_bus_inverted cpl ret .else ret nop ; padding .endif ; ; ; out_fdc_reg_2_sector: .if fdc_data_bus_inverted cpl ; for fdc data bus inversion out (fdc_reg_2_sector),a cpl ; for fdc data bus inversion ret .else out (fdc_reg_2_sector),a ret nop ; padding nop ; padding .endif ; ; ; inp_fdc_reg_2_sector: in a,(fdc_reg_2_sector) .if fdc_data_bus_inverted cpl ret .else ret nop ; padding .endif ; ; ; out_fdc_reg_3_data: .if fdc_data_bus_inverted cpl ; for fdc data bus inversion out (fdc_reg_3_data),a cpl ; for fdc data bus inversion ret .else out (fdc_reg_3_data),a ret nop ; padding nop ; padding .endif ; ; ; inp_fdc_reg_3_data: in a,(fdc_reg_3_data) .if fdc_data_bus_inverted cpl ret .else ret nop ; padding .endif ; ; 0308h_ ; fdc_fn_write_command_to_alias: .if fdc_data_bus_inverted cpl ; for fdc data bus inversion ld c,fdc_reg_0_command_alias out (fdc_reg_0_command),a loop_030dh: in b,(c) ret m in a,(fdc_reg_3_data) cpl ; for fdc data bus inversion ld (hl),a inc hl jr loop_030dh .else ld c,fdc_reg_0_command_alias out (fdc_reg_0_command),a loop_030dh: in b,(c) ret m in a,(fdc_reg_3_data) ld (hl),a inc hl jr loop_030dh nop ; padding nop ; padding .endif ; ; 0317h ; fdc_fn_write_command: .if fdc_data_bus_inverted cpl ; for fdc data bus inversion ld c,fdc_reg_0_command_alias out (fdc_reg_0_command),a .else ld c,fdc_reg_0_command_alias out (fdc_reg_0_command),a nop ; padding .endif ; loop_031ch: .if fdc_data_bus_inverted ld a,(hl) cpl ; for fdc data bus inversion in b,(c) ret m out (fdc_reg_3_data),a inc hl jr loop_031ch .else ld a,(hl) in b,(c) ret m out (fdc_reg_3_data),a inc hl jr loop_031ch nop ; padding .endif ; ; 0326h ; fdc_fn_command_sector_read: and %00011100 ; A:= 000xxx00 or %10000010 ; A:= 100xxx10 call fdc_fn_write_command_to_alias jp jump_to_inp_fdc_reg_0 ; ; 0330h ; fdc_fn_command_sector_write: and %00011101 ; %000xxx0x or %10100010 ; %101xxx1x call fdc_fn_write_command jp jump_to_inp_fdc_reg_0 ; ; ; crlf: .db ASC_CR,ASC_LF,0 asciiz_Press_RETURN: .db ASC_CR,ASC_LF .db "Press RETURN", 0 msg_Arcom_ATLAS: .db ASC_CR,ASC_LF,ASC_LF .db "Arcom ATLAS" ; no zero .db ASC_CR,ASC_LF .db "Serial console running at ", 0 msg_SP_baud_CR_LF: .db " baud", ASC_CR,ASC_LF,0 msg_promtp_for_disk: l037fh: .db ASC_CR,ASC_LF .db "Insert system disk in drive A: and press RETURN",0 table_of_points_to_baud_rate_strings: .dw asc_query .dw str_50 .dw str_75 .dw str_110 .dw str_134_point_5 .dw str_150 .dw str_300 .dw str_600 .dw str_1200 .dw str_1800 .dw str_2400 .dw str_3600 .dw str_4800 .dw str_7200 .dw str_9600 .dw str_19200 ; BLOCK 'baud_rate_message' (start 0x03d1 end 0x04e0) baud_rate_message_start: ;03d1 ; ; Prompt for baud rates: ; asc_query: .db "?",$00 str_50: .db "50",$00 str_75: .db "75",$00 str_110: .db "110",$00 str_134_point_5: .db "134.5",$00 str_150: .db "150",$00 str_300: .db "300",$00 str_600: .db "600",$00 str_1200: .db "1200",$00 str_1800: .db "1800",$00 str_2400: .db "2400",$00 str_3600: .db "3600",$00 str_4800: .db "4800",$00 str_7200: .db "7200",$00 str_9600: .db "9600",$00 str_19200: .db "19200",$00 ; msg_Unknown_disk_format: .db ASC_CR,ASC_LF .db ASC_BEL .db "Unknown disk format",0 l042fh: .db ASC_CR,ASC_LF .db ASC_BEL .db "No system on disk", 0 msg_Error_reading_system_disk: .db ASC_CR,ASC_LF .db ASC_BEL .db "Error reading system disk:" .db ASC_CR,ASC_LF .db "Track-",0 ; msg_comma_space_Side_dash: .db ", Side-",0 ; msg_comma_space_Sector_dash: .db ", Sector-",0 ; ; 047ch ; error_message_pointer_table: .dw msg_comma_space_Not_ready .dw msg_comma_space_Write_protect .dw msg_comma_space_Fault .dw msg_comma_space_Record_not_found .dw msg_comma_space_CRC .dw msg_comma_space_Lost_data ; msg_comma_space_Not_ready: .db ", Not ready",0 ; msg_comma_space_Write_protect: .db ", Write protect",0 ; msg_comma_space_Fault: .db ", Fault",0 ; msg_comma_space_Record_not_found: .db ", Record not found",0 ; msg_comma_space_CRC: .db ", CRC",0 ; msg_comma_space_Lost_data: .db ", Lost data",0 ; msg_comma_Retry_yes_or_no_query: .db ", Retry (Y/N)?",ASC_SP,0 ; .ORG $04E1 routines_to_return_to: .dw $FFF7 .dw $FFFB .dw $FFFA .dw $FFF8 .dw $FFF4 .dw $FFF1 .dw $FFEA .dw $FFC9 .dw $FF7E .dw $FEF9 .dw $FEFE .dw $FF7D .dw $FECA .dw $FDB2 .dw L8000h device_0_crlf: ld hl,crlf ;04ff ; device_0_chars_at_hl: ld a,(hl) ; a = (hl++) inc hl and a ; is it zero? ret z ; return if null terminated call JPCO0 ; device 0 output jr device_0_chars_at_hl ; ; ; START: ld a,0f8h out (control_latch),a ld sp,L80a1h ld hl,block_move_07f3 ld de,L8000h ld bc,0014h ; b=0 moves 256 bytes, c = 14h = 20 bytes ldir ld a,0c0h ld b,9 ld c,scc_port_a_control out (c),b ; register 9 out (c),a ; receives $c0 xor a out (c),b ; register 9 out (c),a ; receives 0 ld a,($40) push af ; call JPCIT0 ld a,(AUTO) ; do auto-baud? and a ; jr z,sign_on ; if not, jump to sign-on ld hl,asciiz_Press_RETURN ; tell user to press return key call device_0_chars_at_hl ; ld hl,RESET di ; loop_0543h: in a,(scc_port_a_status) and mask_bit_4 jr z,loop_0543h ; loop_0549h: inc hl in a,(scc_port_a_status) and mask_bit_4 jr nz,loop_0549h ei push hl ; loop_0552h: ld a,20 ; loop_0554h: ; a software delay? dec a jr nz,loop_0554h dec hl ld a,h or l jr nz,loop_0552h pop hl ld a,16 ; di ; interrupts off ld sp,routines_to_return_to ; loop_0563h: dec a pop de add hl,de jr c,loop_0563h ld sp,L80a1h ei ;interrupts on push af call JPCIT0 ; loop_0570h: call JPCIS0 jr z,sign_on call JPCI0 jr loop_0570h ; ; l057ah ; sign_on: ld hl,msg_Arcom_ATLAS ; ... running at xxxx baud call device_0_chars_at_hl pop af ; pull A = baud rate option 0 to 15 add a,a ; double it to get an index ld c,a ; into table of words ld b,0 ld hl,table_of_points_to_baud_rate_strings add hl,bc ; get pointer address ld a,(hl) ; read lsb inc hl ld h,(hl) ; read msb ld l,a call device_0_chars_at_hl ; print baud rate ld hl,msg_SP_baud_CR_LF ; print " baud" call device_0_chars_at_hl loop_0596h: ld hl,l037fh call device_0_chars_at_hl loop_059ch: call JPCI0 cp ASC_CR ; ignore jr nz,loop_059ch call device_0_crlf call sub_05f8h jr z,l05b3h ; ld hl,msg_Unknown_disk_format call device_0_chars_at_hl jr loop_0596h ; ; ; l05b3h: ld hl,L80a1h xor a ; a :=0 ; loop_05b7h: ld (L8019h),a ; clear xor a ; ; loop_05bbh: inc a ld (L801ah),a call sub_06aah ld a,(L8017h) ld e,a ld a,(L801ah) cp e jr nz,loop_05bbh ld a,(SYSTRK_remembered) ld e,a ld a,(L8019h) inc a cp e jr nz,loop_05b7h ; ld de,L80a1h xor a sbc hl,de ld b,h ld c,l ex de,hl or c jr z,l05e5h ; inc b xor a ; ; ; l05e5h: add a,(hl) ;05e5 inc hl dec c jr nz,l05e5h djnz l05e5h inc a jp z,L8000h ld hl,l042fh call device_0_chars_at_hl jr loop_0596h ; ; ; sub_05f8h: ld a,(SIDES) ld (SIDES_remembered),a ld a,(SYSTRK) ld (SYSTRK_remembered),a ld a,(SECSIZ) ld b,a ld a,(SECPT) inc b add a,a l060dh: rra djnz l060dh ; ld (L8017h),a ld a,(TYPE) ld (control_latch_remembered),a xor a ld (L8019h),a dec a ld (L8018h),a call sub_073bh ret nz ; ld a,(control_latch_remembered) ld e,a and 080h ret z ; ld a,(NTRKS) cp 04dh jr z,l0635h ; xor a ret ; ; ; l0635h: ld a,e ;0635 and 0f8h ;0636 or 018h ;0638 ld (control_latch_remembered),a xor a ;063d ld (SIDES_remembered),a ;063e ld a,002h ;0641 ld (SYSTRK_remembered),a ;0643 ld a,01ah ;0646 ld (L8017h),a ;0648 call sub_0684h ;064b jr nz,l0655h ;064e ld a,(L801eh) ;0650 and a ;0653 ret ; ; ; l0655h: ld a,(control_latch_remembered) and %11110000 ; bits 3..0 cleared ld (control_latch_remembered),a ld a,(SIDES) ld (SIDES_remembered),a ld a,(SYSTRK) ld (SYSTRK_remembered),a ld a,(SECSIZ) ld b,a ld a,(SECPT) inc b add a,a l0672h: rra djnz l0672h ; ld (L8017h),a call sub_0684h ret nz ; ld a,(L801eh) ld hl,SECSIZ cp (hl) ret ; ; ; sub_0684h: ld b,004h l0686h: call sub_073bh ret nz ; push bc ;068a ld a,(control_latch_remembered) out (control_latch),a ld hl,0801bh ;0690 ld a,0c4h ;0693 call jump_to_fdc_fn_write_command_to_alias pop bc ;0698 call jump_to_inp_fdc_reg_0 and 0f0h ;069c ret z ; dec b jr nz,l06a5h and 0f0h ret ; ; ; l06a5h: call sub_072ah jr l0686h ; ; ; sub_06aah: ld b,4 loop_06ach: call sub_073bh jr nz,skip_06d5h ; push bc push hl ld a,(L801ah) call jump_to_out_fdc_reg_2_sector ld a,(control_latch_remembered) out (control_latch),a and mask_bit_4 rrca xor mask_bit_3 ; di call jump_to_fdc_fn_command_sector_read ei ; pop de pop bc and 0fch ret z ; ex de,hl ld de,loop_06ach push de djnz sub_072ah pop de ; skip_06d5h: push hl push af ld hl,msg_Error_reading_system_disk call device_0_chars_at_hl ld a,(L8019h) call print_register_A_decimal ld hl,msg_comma_space_Side_dash call device_0_chars_at_hl ld a,(control_latch_remembered) and mask_bit_4 ld a,"0" jr nz,skip_06f3h ; inc a skip_06f3h: call JPCO0 ld hl,msg_comma_space_Sector_dash call device_0_chars_at_hl ld a,(L801ah) call print_register_A_decimal pop af ld hl,error_message_pointer_table loop_0706h: ld e,(hl) ; de := pointer inc hl ld d,(hl) inc hl add a,a ;double the indexing value push af ex de,hl call c,device_0_chars_at_hl ex de,hl pop af jr nz,loop_0706h ld hl,msg_comma_Retry_yes_or_no_query call device_0_chars_at_hl call 007dbh pop hl cp 3 jp z,loop_0596h cp 059h jr z,sub_06aah ; ld a,1 ret ; ; ; sub_072ah: ld a,(L8019h) bit 0,b jr nz,l0737h and a jr z,l0737h xor a ; clear A jr l073eh ; ; ; l0737h: add a,2 jr l073eh ; ; ; sub_073bh: ld a,(L8019h) l073eh: ld e,a ld a,(SIDES_remembered) and a ld a,(control_latch_remembered) jr z,l0750h ; or mask_bit_4 srl e jr nc,l0750h ; and 0e8h l0750h: ld (control_latch_remembered),a or 090h out (control_latch),a ld a,(L8018h) push af call jump_to_out_fdc_reg_1_track pop af call jump_to_out_fdc_reg_3_data ld a,018h call sub_07a8h push bc ld b,0c8h jr l0771h ; ; ; loop_076ch: ld a,10 call delay_A_microseconds ; ; ; l0771h: ld a,018h call sub_07a8h and mask_bit_7 jr z,l077eh ; djnz loop_076ch pop bc ret ; ; ; l077eh: pop bc ld a,(L8018h) ;077f sub e ret z ; ld a,e ld (L8018h),a ; push af call jump_to_out_fdc_reg_3_data pop af ; and a ld a,018h ;078e jr nz,l0794h ;0790 ; ld a,8 l0794h: call sub_07a8h push af ld a,50 ; fifty microseconds call delay_A_microseconds pop af ret ; ; some kind of software delay. ; ; 079fh: delay_A_microseconds: ld d,250 ; number of inner loops ; loop_4_microseconds: ; 4 microseconds per loop apart from last one dec d ; 4 cyles, 1 us at 4 MHz jr nz,loop_4_microseconds ; 12 or 7 cyles, 3 or 1.75 us at 4 MHz ; dec a ; 4 cyles, 1 us at 4 MHz jr nz,delay_A_microseconds ret ; ; ; sub_07a8h: push de ld d,a ld a,(STEPS) or d call jump_to_out_fdc_reg_0 pop de ld a,12 ; 12 microseconds ; loop_A_microseconds: dec a ; 4 microseconds per loop apart from last one jr nz,loop_A_microseconds ; loop_07b7h: call jump_to_inp_fdc_reg_0 bit 0,a jr nz,loop_07b7h and %10011000 ; 098h ret ; ; 07c1h: ; print_register_A_decimal: ld hl,decimal_weights ld b,3 ; 3 bytes of data ; loop_07c6h: ld d,(hl) ; fetch 100, 10 then 1 ld c,02fh ; = '0' - 1 ; loop_07c9h: inc c sub d jr nc,loop_07c9h ; subtract until negative add a,d ; make positive again ; ld e,a ; save remainder ld a,c ; c is how many times it could subtract 100, 10 or 1 call JPCO0 ; char to console ld a,e ; restore remainder inc hl djnz loop_07c6h ret ; ; 07d8h ; decimal_weights: .db 100, 10, 1 ; $64,$0A,$01 ; ; ; loop_07dbh: call JPCIS0 jr z,skip_07e5h ; call JPCI0 ; get char jr loop_07dbh ; ignore nulls ; ; ; skip_07e5h: call JPCI0 cp ASC_SP call nc,JPCO0 cp "a" ret c ; sub ASC_SP ret ; ; ; block_move_07f3: ld a,(control_latch_remembered) inc a ; increment control latch out (control_latch),a ld hl,L80a1h ld de,CPM_TPA ; CP/M Transient Program Area? ld bc,L4000h ; 16K onwards ldir jp CPM_TPA ; ; ; .fill $ff, $810 - $ ; ;.org $810 ; .fill $ff, $2000 - $ .end